package org.unidata.mdm.dq.core.service.impl.instance;

import java.time.OffsetDateTime;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.unidata.mdm.core.type.model.instance.AbstractNamedDisplayableCustomPropertiesImpl;
import org.unidata.mdm.dq.core.type.cleanse.CleanseFunctionExecutionScope;
import org.unidata.mdm.dq.core.type.model.instance.CleanseFunctionElement;
import org.unidata.mdm.dq.core.type.model.instance.PortElement;
import org.unidata.mdm.dq.core.type.model.source.AbstractCleanseFunctionSource;

/**
 * @author Mikhail Mikhailov on Jan 28, 2021
 * Cleanse function base.
 */
public abstract class AbstractCleanseFunctionImpl<X extends AbstractCleanseFunctionSource<X>>
    extends AbstractNamedDisplayableCustomPropertiesImpl
    implements CleanseFunctionElement {
    /**
     * This function's source.
     */
    protected final X source;
    /**
     * Input ports.
     */
    protected final Map<String, PortElement> input = new HashMap<>();
    /**
     * Output ports.
     */
    protected final Map<String, PortElement> output = new HashMap<>();
    /**
     * Supported execution scopes.
     */
    protected final Set<CleanseFunctionExecutionScope> scopes;
    /**
     * Constructor.
     */
    protected AbstractCleanseFunctionImpl(X acfs) {
        super(acfs.getName(), acfs.getDisplayName(), acfs.getDescription(), acfs.getCustomProperties());
        this.source = acfs;
        this.scopes = CollectionUtils.isEmpty(acfs.getExecutionScopes())
                ? EnumSet.noneOf(CleanseFunctionExecutionScope.class)
                : EnumSet.copyOf(acfs.getExecutionScopes());
        this.input.putAll(source.getInputPorts().stream()
                .map(PortImpl::new)
                .collect(Collectors.toMap(PortElement::getName, Function.identity())));
        this.output.putAll(source.getOutputPorts().stream()
                .map(PortImpl::new)
                .collect(Collectors.toMap(PortElement::getName, Function.identity())));
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String getId() {
        return getName();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public OffsetDateTime getCreateDate() {
        return source.getCreateDate();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String getCreatedBy() {
        return source.getCreatedBy();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public OffsetDateTime getUpdateDate() {
        return source.getUpdateDate();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String getUpdatedBy() {
        return source.getUpdatedBy();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<PortElement> getInputPorts() {
        return input.values();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<PortElement> getOutputPorts() {
        return output.values();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public PortElement getInputPortByName(String name) {
        return input.get(name);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public PortElement getOutputPortByName(String name) {
        return output.get(name);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isSystem() {
        return source.isSystem();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isReady() {
        return source.isReady();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isConfigurable() {
        return source.isConfigurable();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isEditable() {
        return source.isEditable();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean supports(CleanseFunctionExecutionScope scope) {
        return scopes.contains(scope);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String getNote() {
        return source.getNote();
    }
    /**
     * Returns the source view of the data.
     * @return the source view of the data
     */
    public X getSource() {
        return source;
    }
    /*
     * Sets the note.
     */
    protected void annotate(String note) {
        source.setNote(note);
    }
}
